package com.ndp.fb.walle.business.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSONObject;
import com.ndp.ec.core.FbUserService;
import com.ndp.fb.config.ConfigCentre;
import com.ndp.fb.constant.CommonConstants;
import com.ndp.fb.constant.ConfigConst;
import com.ndp.fb.data.api.service.schedulerservice.business.syncbusiness.SyncBusinessService;
import com.ndp.fb.enums.ErrorType;
import com.ndp.fb.enums.account.AccountOperateType;
import com.ndp.fb.enums.page.PageOperateType;
import com.ndp.fb.exception.BusinessException;
import com.ndp.fb.exception.ErrorCode;
import com.ndp.fb.exception.entrustaccount.FromFBEntrustAccountsIsNullException;
import com.ndp.fb.exception.entrustaccount.GetAccessTokenException;
import com.ndp.fb.exception.entrustaccount.IsNotBusinessesManagerException;
import com.ndp.fb.fbagent.service.AdGroupFBService;
import com.ndp.fb.fbagent.service.OauthFbService;
import com.ndp.fb.fbagent.service.model.FBOperator;
import com.ndp.fb.fbagent.service.model.Filter;
import com.ndp.fb.model.fbagent.result.adgroup.FbAdgroup;
import com.ndp.fb.model.fbagent.result.businesses.FBAdaccountPermission;
import com.ndp.fb.model.fbagent.result.businesses.FBBusinesses;
import com.ndp.fb.model.fbagent.result.businesses.FBMe;
import com.ndp.fb.model.fbagent.result.businesses.FBPagePermission;
import com.ndp.fb.model.fbagent.result.businesses.User;
import com.ndp.fb.model.fbagent.result.businesses.UserpermissionsResult;
import com.ndp.fb.model.fbagent.result.entrustaccount.EntrustAccounts;
import com.ndp.fb.mq.api.producer.AssetsAlertProducerService;
import com.ndp.fb.mq.api.service.schedulerservice.business.syncbusiness.SyncAccountInfoService;
import com.ndp.fb.mq.api.service.schedulerservice.business.syncbusiness.SyncAccountResourceService;
import com.ndp.fb.rdb.api.AccountOperateHistoryRdbService;
import com.ndp.fb.rdb.api.AppInfoRdbService;
import com.ndp.fb.rdb.api.ConnectionObjectRdbService;
import com.ndp.fb.rdb.api.EntrustAccountRdbService;
import com.ndp.fb.rdb.api.FbUserBusinessRdbService;
import com.ndp.fb.rdb.api.FbUserPermissionRdbService;
import com.ndp.fb.rdb.api.FbUserRdbService;
import com.ndp.fb.rdb.api.PagePermissionRdbService;
import com.ndp.fb.rdb.model.AdAccountPermission;
import com.ndp.fb.rdb.model.AppInfo;
import com.ndp.fb.rdb.model.BusinessManager;
import com.ndp.fb.rdb.model.EntrustAccount;
import com.ndp.fb.rdb.model.EntrustAccountExt;
import com.ndp.fb.rdb.model.FbUser;
import com.ndp.fb.rdb.model.FbUserBusinessManager;
import com.ndp.fb.rdb.model.PagePermission;
import com.ndp.fb.redis.api.RedisClient;
import com.ndp.fb.redis.api.RedisDistributedLockService;
import com.ndp.fb.util.DateUtil;
import com.ndp.fb.util.StringUtil;
import com.ndp.fb.walle.business.AdPublishAppAccountCPAService;
import com.ndp.fb.walle.business.BusinessManagerService;
import com.ndp.fb.walle.business.ConnectionObjectService;
import com.ndp.fb.walle.business.EntrustAccountService;
import com.ndp.fb.walle.business.FBOauthService;
import com.ndp.fb.walle.business.ProductConfigurationService;
import com.ndp.fb.walle.model.bo.entrustaccount.ChangeAccountResult;

/**
 * Created by Mikey.zhaopeng on 2014/10/24 16:24.
 * Email mikey.zhao@ndpmedia.com
 */
@Component
public class FBOauthServiceImpl implements FBOauthService {

    private static final Logger logger = LoggerFactory.getLogger(FBOauthServiceImpl.class);

    @Autowired
    BusinessManagerService businessManagerService;

    @Autowired
    EntrustAccountService entrustAccountService;

    @Autowired
    ConnectionObjectService connectionObjectService;

    @Autowired
    ProductConfigurationService productConfigurationService;

    @Autowired
    AdPublishAppAccountCPAService adPublishAppAccountCPAService;

    @Reference
    AppInfoRdbService appInfoRdbService;
    @Reference
    SyncAccountResourceService syncAccountResourceService;
    @Reference
    SyncBusinessService syncBusinessService;
    @Reference
    SyncAccountInfoService syncAccountInfoService;
    @Reference
    FbUserPermissionRdbService fbUserPermissionRdbService;
    @Reference
    AccountOperateHistoryRdbService accountOperateHistoryRdbService;
    @Reference
    FbUserBusinessRdbService fbUserBusinessRdbService;
    @Reference
    PagePermissionRdbService pagePermissionRdbService;
    @Reference
    FbUserService fbUserService;
    @Reference
    FbUserRdbService fbUserRdbService;
    @Autowired
    RedisDistributedLockService redisDistributedLockService;
    @Reference
    EntrustAccountRdbService entrustAccountRdbService;

    @Reference
    AdGroupFBService adGroupFBService;
    @Autowired
    private RedisClient redisClient;
    @Reference
    private OauthFbService oauthFbService;
    @Reference
    private ConnectionObjectRdbService connectionObjectRdbService;
    @Autowired
    private AssetsAlertProducerService assetsAlertProducerService;

    private String redirectUri = ConfigCentre.getString(ConfigConst.WALLE_OAUTH_REDIRECTURI);
    private String locale = ConfigCentre.getString(ConfigConst.WALLE_OAUTH_LOCALE);
    private String resultRedirectUri = ConfigCentre.getString(ConfigConst.WALLE_OAUTH_RESUALTREDIRECTURI);

    @Override
    public List<BusinessManager> syncBusinessManager(String accessToken) throws  IsNotBusinessesManagerException {
        List<BusinessManager> businessManagers = null;
        try{
            BusinessManager businessManager = null;
            List<FBBusinesses> fbBusinessesList = oauthFbService.getFbBusinessesList(accessToken);
            if (!CollectionUtils.isEmpty(fbBusinessesList)) {
                businessManagers = new ArrayList<>();
                for (FBBusinesses fbBusinesses : fbBusinessesList) {
                    if (fbBusinesses != null && StringUtil.isNotEmpty(fbBusinesses.getId())) {
                        //防止重复插入，加上分布式同步锁
                        businessManager = new BusinessManager(Long.parseLong(fbBusinesses.getId()), fbBusinesses.getName(), new Date());
                        String lockKey = CommonConstants.BUSINESS_MANAGE_LOCK + "-" + fbBusinesses.getId();
                        logger.info("start lock businessManger save thread,bm:" + fbBusinesses.getId());
                        if (redisDistributedLockService.tryLock(lockKey)) {
                            try {
                                if (businessManagerService.findByFbBusinessId(Long.parseLong(fbBusinesses.getId())) == null) {
                                    businessManagerService.saveBusinessManager(businessManager);
                                }
                            } catch (Exception ex) {
                                logger.error("save businessManager error,bm id:" + fbBusinesses.getId());
                                ex.printStackTrace();
                            } finally {
                                logger.info("unlock businessManger save thread,bm:" + fbBusinesses.getId());
                                redisDistributedLockService.unLock(lockKey);
                            }
                        }
                        businessManagers.add(businessManager);
                    } else {
                        logger.error("and bussinessManager fail! accessToken: " + accessToken);
                        throw new IsNotBusinessesManagerException();
                    }
                }
            }
        }catch(Exception ex){
            logger.info("call facebook interface error!",ex);
        }
        return businessManagers;
    }

    @Override
    public String getAccessToken(String code) throws GetAccessTokenException {

        String accessToken = null;
        try {
            AppInfo appInfo = appInfoRdbService.getAppInfo();
            accessToken = oauthFbService.getAccessToken(appInfo.getAppId(),appInfo.getAppSecret(), code, redirectUri, locale);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new GetAccessTokenException();
        }
        return accessToken;
    }

    @Override
    public String getRedirectUrl(String resultFlag) {
        return this.resultRedirectUri;
    }

    /**
     * 检查用户是否已经关联了fbuser
     *
     * @param userId
     * @return
     */
    @Override
    public Boolean isCanAddFbUser(String userId) throws BusinessException {
        if (StringUtil.isEmpty(userId)) {
            logger.error("user is null!");
            return false;
        }
        //2.一个用户只能添加一个fbuser账号
        FbUser fbUser = fbUserService.getFbUser(userId);
        if (null != fbUser) {
            return false;
        }
        return true;
    }

    /**
     * 同步广告帐号信息
     *
     * @param accessToken
     * @param userId
     */
    private List<EntrustAccountExt> syncEntrustAccountInformation(String accessToken, String userId) throws FromFBEntrustAccountsIsNullException {

        List<EntrustAccounts> facebookEntrustAccounts = oauthFbService.getEntrustAccountsByFbUser(accessToken, 0, CommonConstants.NUM_INT_500, true);

        if (facebookEntrustAccounts == null || facebookEntrustAccounts.size() == 0) {
            logger.error("get data from facebook entrustAccounts is empty!");// ????
            throw new FromFBEntrustAccountsIsNullException();
        }

        //Status of the account.
       /* 1 = Active
        2 = Disabled
        3 = Unsettled
        7 = Pending Review
        9 = In Grace Period
        101 = temporarily unavailable
        100 = pending closure.*/
        List<EntrustAccountExt> entrustAccountList = this.FBEntrustAccount2EntrustAccount(facebookEntrustAccounts);

        //做一次 调用消息队列  同步图片
//        try {
//            logger.info("send syncAccountImageByUserId message start!");
//            syncAccountInfoService.syncAccountInfo(userId);
//            logger.info("send syncAccountImageByUserId message end!");
//        } catch (Exception e) {
//            logger.error(e.getMessage(), e);
//        }
        return entrustAccountList;

    }

    /**
     * 将FB广告帐号转为系统使用的广告帐号
     *
     * @param entrustAccounts
     * @return
     */
    private List<EntrustAccountExt> FBEntrustAccount2EntrustAccount(List<EntrustAccounts> entrustAccounts) {
        List<EntrustAccountExt> entrustAccountList = new ArrayList<>();
        for (EntrustAccounts fbEntrustAccount : entrustAccounts) {
            EntrustAccountExt entrustAccount = new EntrustAccountExt();
            entrustAccount.setFbAccountId(Long.parseLong(fbEntrustAccount.getAccountId()));
            entrustAccount.setTimezone(fbEntrustAccount.getTimezoneName());
            entrustAccount.setAccountStatus(fbEntrustAccount.getAccountStatus());
            entrustAccount.setName(fbEntrustAccount.getName());
            entrustAccount.setBalance(fbEntrustAccount.getBalance());
            entrustAccount.setCreateTime(new Date());
            entrustAccount.setUpdateTime(new Date());
            entrustAccount.setRole(fbEntrustAccount.getRole());
            entrustAccount.setStatus(fbEntrustAccount.getAccessStatus());
            entrustAccountList.add(entrustAccount);
        }
        return entrustAccountList;
    }

    /**
     * 获取 BM下面取到的广告权限
     * <p/>
     * 调用 apiagent 调用 {businessId}/userpermissions?fields=adaccount_permissions ,验证 广告帐号获取的权限,
     * <p/>
     * 过滤条件 "ADMIN".equals(adaccountPermission.getRole()) && "ACTIVE".equals(adaccountPermission.getStatus())
     *
     * @return
     */
    private List<PagePermission> getPagePermissionList(String accessToken, BusinessManager bm, Long fbUserId, List<UserpermissionsResult> userpermissionsResults) {
        List<PagePermission> pagePermissionList = new ArrayList<>();
        if (userpermissionsResults == null || userpermissionsResults.size() == 0) {
            return pagePermissionList;
        }

        for (UserpermissionsResult userpermissionsResult : userpermissionsResults) {
            if (userpermissionsResult != null) {

                User user = userpermissionsResult.getUser();
                if (user == null || StringUtil.isEmpty(user.getId())) {
                    continue;
                }

                String strFbUserId = String.valueOf(fbUserId);

                if (!strFbUserId.equals(user.getId())) {
                    continue;
                }
                PagePermission pagePermission = null;
                List<FBPagePermission> fbPagePermissions = userpermissionsResult.getPagePermissions();
                if (!CollectionUtils.isEmpty(fbPagePermissions)) {
                    for (FBPagePermission fbPagePermission : fbPagePermissions) {
                        if (fbPagePermission != null) {
                            //只导入状态为ACTIVE的账号
                            String status = StringUtil.isEmpty(fbPagePermission.getStatus()) ? "" : fbPagePermission.getStatus();
                            if ("ACTIVE".equals(status)) { //
                                pagePermission = new PagePermission();
                                String role = StringUtil.isEmpty(fbPagePermission.getRole()) ? "" : fbPagePermission.getRole();
                                pagePermission.setPageId(fbPagePermission.getId());
                                pagePermission.setFbBusinessId(bm.getFbBusinessesId());
                                pagePermission.setFbUserId(fbUserId);
                                pagePermission.setRole(role);
                                pagePermission.setStatus(status);
                                pagePermissionList.add(pagePermission);
                            }
                        }
                    }
                }
            }
        }
        return pagePermissionList;

    }

    /**
     * 获取 BM下面取到的广告权限
     * <p/>
     * 调用 apiagent 调用 {businessId}/userpermissions?fields=adaccount_permissions ,验证 广告帐号获取的权限,
     * <p/>
     * 过滤条件 "ADMIN".equals(adaccountPermission.getRole()) && "ACTIVE".equals(adaccountPermission.getStatus())
     *
     * @return
     */
    private List<AdAccountPermission> getAdaccountPpermissionList(BusinessManager bm, Long fbUserId, List<UserpermissionsResult> userpermissionsResults) {

        List<AdAccountPermission> adaccountPermissionList = new ArrayList<>();
        Map<String, AdAccountPermission> adaccountPermissionMap = new HashMap<String, AdAccountPermission>();

        if (userpermissionsResults == null || userpermissionsResults.size() == 0) {
            return adaccountPermissionList;
        }

        for (UserpermissionsResult userpermissionsResult : userpermissionsResults) {

            if (userpermissionsResult != null) {

                User user = userpermissionsResult.getUser();
                if (user == null || StringUtil.isEmpty(user.getId())) {
                    continue;
                }

                String strFbUserId = String.valueOf(fbUserId);

                if (!strFbUserId.equals(user.getId())) {
                    continue;
                }

                FbUserBusinessManager fbUserBusinessManager = new FbUserBusinessManager();
                fbUserBusinessManager.setFbUserId(fbUserId);
                fbUserBusinessManager.setFbBusinessId(bm.getFbBusinessesId());
                fbUserBusinessManager.setRole(userpermissionsResult.getRole());
                fbUserBusinessManager.setStatus(userpermissionsResult.getStatus());
                fbUserBusinessRdbService.saveFbUserBusinessManager(fbUserBusinessManager);

                AdAccountPermission userPermission = null;
                List<FBAdaccountPermission> adaccountPermissions = userpermissionsResult.getAdaccountPermissions();
                if (adaccountPermissions != null && adaccountPermissions.size() > 0) {
                    for (FBAdaccountPermission adaccountPermission : adaccountPermissions) {
                        if (adaccountPermission != null) {
                            String status = StringUtil.isEmpty(adaccountPermission.getStatus()) ? "" : adaccountPermission.getStatus();
                            if ("ACTIVE".equals(status)) {
                                String role = StringUtil.isEmpty(adaccountPermission.getRole()) ? "" : adaccountPermission.getRole();
                                Long accountId = Long.valueOf(adaccountPermission.getId().replace("act_", ""));
                                userPermission = new AdAccountPermission();
                                userPermission.setFbAccountId(accountId);
                                userPermission.setFbBusinessId(bm.getFbBusinessesId());
                                userPermission.setFbUserId(fbUserId);
                                userPermission.setRole(role);
                                userPermission.setStatus(status);
                                adaccountPermissionMap.put(adaccountPermission.getId(), userPermission);
                            }
                        }
                    }
                }

            }
        }

        Set set = adaccountPermissionMap.entrySet();
        for (Iterator it = set.iterator(); it.hasNext(); ) {
            Map.Entry entry = (Map.Entry) it.next();
            adaccountPermissionList.add((AdAccountPermission) entry.getValue());
        }
        return adaccountPermissionList;

    }

    /**
     * 比较广告权限帐号和 entrustAccount 广告帐号,如果 act_{id} 相等和 account_status=1, 则数据入库,否则舍弃
     *
     * @param fbEntrustAccountsList
     * @param adaccountPpermissionList
     * @return
     */
    private List<EntrustAccounts> compareEntrustAccountListAndAdaccountPpermissionList(List<EntrustAccounts> fbEntrustAccountsList, List<AdAccountPermission> adaccountPpermissionList, Integer accountStatus) {
        List<EntrustAccounts> resultList = new ArrayList<>();
        for (AdAccountPermission adaccountPpermission : adaccountPpermissionList) {
            for (EntrustAccounts fbEntrustAccounts : fbEntrustAccountsList) {
                if ((fbEntrustAccounts.getAccountId()).equals(adaccountPpermission.getFbAccountId().toString()) && fbEntrustAccounts.getAccountStatus().equals(accountStatus)) {
                    fbEntrustAccounts.setRole(adaccountPpermission.getRole());
                    resultList.add(fbEntrustAccounts);
                    break;
                }

            }
        }
        return resultList;
    }


    private List<EntrustAccountExt> getUpdateEntrustAccount(Map<Long,AdAccountPermission> userPermissions,List<EntrustAccountExt> entrustAccountList) {
        List<EntrustAccountExt> result = new ArrayList<EntrustAccountExt>();
        AdAccountPermission userPermission = null;
        for(EntrustAccountExt entrustAccountExt:entrustAccountList){
            if(userPermissions.containsKey(entrustAccountExt.getFbAccountId())){
                userPermission = userPermissions.get(entrustAccountExt.getFbAccountId());
                if(!userPermission.getRole().equals(entrustAccountExt.getRole())){
                    entrustAccountExt.setRole(userPermission.getRole());
                    entrustAccountExt.setStatus(userPermission.getStatus());
                    result.add(entrustAccountExt);
                    continue;
                }
                if(!userPermission.getStatus().equals(entrustAccountExt.getStatus())){
                    entrustAccountExt.setStatus(userPermission.getStatus());
                    result.add(entrustAccountExt);
                }
            }
        }
        return result;
    }

    @Override
    public ChangeAccountResult getChangeEntrustAccount(String userId) throws FromFBEntrustAccountsIsNullException, BusinessException {
        ChangeAccountResult result = new ChangeAccountResult();
        String accessToken = null;
        FbUser fbUser = fbUserService.getFbUser(userId);
        ErrorType errorType = null;
        if (fbUser == null) {
            errorType = ErrorType.AD_ACCOUNTS_SYNC_FAILURE;
        } else {
            accessToken = fbUser.getAccessToken();
            try {
                if (StringUtil.isEmpty(accessToken)) {
                    errorType = ErrorType.GET_ACCESS_TOKEN_FAIL;
                } else {

                    //同步更新facebook user
                    syncUpdateFbUser(accessToken, fbUser);

                    //初始化所有变量
                    List<AdAccountPermission> fbAdaccountPpermissionList = null;
                    Map<Long,AdAccountPermission> mapFBAdAccountPermissions = null;
                    List<EntrustAccountExt> appendAccounts = new ArrayList<>();
                    List<EntrustAccountExt> updateAccounts = new ArrayList<>();

                    List<BusinessManager> fbBusinessManagers = syncBusinessManager(accessToken);
                    List<EntrustAccountExt> recycleAccounts = getRecycleAccount(userId, fbUser, fbBusinessManagers);

                    //处理facebook返回来的所有广告账号
                    if (!CollectionUtils.isEmpty(fbBusinessManagers)) {

                        //获取当前accessToken下的所有广告账号
                        List<EntrustAccounts> fbEntrustAccountes = oauthFbService.getEntrustAccountsByFbUser(accessToken, 0, CommonConstants.NUM_INT_500, true);

                        // 同步BM信息以及BM下账号信息
                        for (BusinessManager businessManager : fbBusinessManagers) {
                            List<UserpermissionsResult> userpermissionsResults = oauthFbService.getUserpermissionsResult(accessToken, businessManager.getFbBusinessesId(), 0, CommonConstants.NUM_INT_500, true);
                            fbAdaccountPpermissionList = this.getAdaccountPpermissionList(businessManager, fbUser.getFbUserId(), userpermissionsResults);
                            if (CollectionUtils.isEmpty(fbAdaccountPpermissionList)) {
                                fbAdaccountPpermissionList = new ArrayList<>();
                            }
                            mapFBAdAccountPermissions = new HashMap<Long, AdAccountPermission>();

                            //获取当前bm下该用户的广告账号
                            List<EntrustAccountExt> entrustAccountDBList = entrustAccountService.findEntrustAccountsByUserId(userId, businessManager.getFbBusinessesId());
                            if (CollectionUtils.isEmpty(entrustAccountDBList)) {
                                entrustAccountDBList = new ArrayList<>();
                            }

                            for(AdAccountPermission adAccountPermission:fbAdaccountPpermissionList){
                                mapFBAdAccountPermissions.put(adAccountPermission.getFbAccountId(), adAccountPermission);
                            }

                            List<EntrustAccounts> fbActiveEntrustAccountses = compareEntrustAccountListAndAdaccountPpermissionList(fbEntrustAccountes, fbAdaccountPpermissionList, 1);
                            appendAccounts.addAll(this.getAppendEntrustAccount(fbActiveEntrustAccountses, entrustAccountDBList));
                            recycleAccounts.addAll(this.getRecyleEntrustAccount(fbActiveEntrustAccountses,entrustAccountDBList));
                            updateAccounts.addAll(this.getUpdateEntrustAccount(mapFBAdAccountPermissions, entrustAccountDBList));
                        }
                    }

                    result.setAppendAccounts(appendAccounts);
                    result.setRecycleAccounts(recycleAccounts);
                    result.setUpdateAccounts(updateAccounts);
                }
            } catch (IsNotBusinessesManagerException e) {
                errorType = ErrorType.IS_NOT_BUSINESSES_MANAGER_EXCEPTION;
            } catch (Exception e) {
                errorType = ErrorType.INTERNAL_EXCEPTION;
                logger.error(e.getMessage(), e);
            }
        }
        if (null != errorType) {
            throw new BusinessException(errorType);
        }
        return result;
    }

    private void syncUpdateFbUser(String accessToken, FbUser fbUser) {
        FBMe fbMe = oauthFbService.getFbMe(accessToken);
        String fbUserId = fbMe.getId();
        fbUser.setAccessToken(accessToken);
        fbUser.setEmail(fbMe.getEmail());
        fbUser.setFbUserId(Long.valueOf(fbUserId));
        fbUser.setFirstName(fbMe.getFirstName());
        fbUser.setGender(fbMe.getGender());
        fbUser.setLastName(fbMe.getLastName());
        fbUser.setLink(fbMe.getLink());
        fbUser.setLocale(fbMe.getLocale());
        fbUser.setName(fbMe.getName());
        String strOffset = fbMe.getTimezone()>=0?("+"+String.format("%02d", fbMe.getTimezone())+":00"):(String.format("%03d", fbMe.getTimezone())+":00");
        fbUser.setTimeZone("GMT"+strOffset);
        fbUser.setUpdatedTime(DateUtil.FbDateToJavaDate(fbMe.getUpdatedTime()).getTime());
        fbUser.setVerified(1);
        fbUserService.updateFbUser(fbUser);
    }

    /**
     * 获取需要回收的广告账号
     * @param userId
     * @param fbUser
     * @param fbBusinessManagers
     * @return
     */
    private List getRecycleAccount(String userId, FbUser fbUser, List<BusinessManager> fbBusinessManagers) {
        List<EntrustAccountExt> recycleAccounts = new ArrayList<>();

        Map<Long,BusinessManager> fbBmMap = new HashMap<>();
        if(!CollectionUtils.isEmpty(fbBusinessManagers)){
            for(BusinessManager businessManager:fbBusinessManagers){
                fbBmMap.put(businessManager.getFbBusinessesId(), businessManager );
            }
        }


        List<FbUserBusinessManager> dbFbUserBusinessManagers = fbUserBusinessRdbService.getFbUserBusinessManagersByFbUserId(fbUser.getFbUserId());
        List<BusinessManager> dbBusinessManagers = new ArrayList<BusinessManager>();
        dbFbUserBusinessManagers.forEach(fbUserBusinessManager->{
            BusinessManager businessManager = businessManagerService.findByFbBusinessId(fbUserBusinessManager.getFbBusinessId());
            if(businessManager !=null ){
                dbBusinessManagers.add(businessManager);
            }
        });

        //找出数据库有的，facebook没有的企业的所有广告账号，回收
        if(!CollectionUtils.isEmpty(dbBusinessManagers)){
            for(BusinessManager businessManager:dbBusinessManagers){
                if(!fbBmMap.containsKey(businessManager.getFbBusinessesId())){
                    List<EntrustAccountExt> entrustAccountList = entrustAccountService.findEntrustAccountsByUserId(userId, businessManager.getFbBusinessesId());
                    if(!CollectionUtils.isEmpty(entrustAccountList)){
                        recycleAccounts.addAll(entrustAccountList);
                    }
                }
            }
        }
        return recycleAccounts;
    }


    private List<EntrustAccountExt> getAppendEntrustAccount(List<EntrustAccounts> activeEntrustAccountList, List<EntrustAccountExt> dbEntrustAccountses) {
        List<EntrustAccounts> resultEntrustAccountsList = new ArrayList<>();
        for (EntrustAccounts entrustAccounts : activeEntrustAccountList) {
            boolean isFounded = false;
            for (EntrustAccountExt entrustAccount : dbEntrustAccountses) {
                if (entrustAccount.getFbAccountId().toString().equals(entrustAccounts.getAccountId())) {
                    isFounded = true;
                    break;
                }
            }
            if (!isFounded) {
                resultEntrustAccountsList.add(entrustAccounts);
            }
        }

        return this.FBEntrustAccount2EntrustAccount(resultEntrustAccountsList);
    }


    /**
     * 获取回收广告账号
     * @param fbActiveEntrustAccountList
     * @param dbEntrustAccountses
     * @return
     */
    private List<EntrustAccountExt> getRecyleEntrustAccount(List<EntrustAccounts> fbActiveEntrustAccountList, List<EntrustAccountExt> dbEntrustAccountses) {
        List<EntrustAccountExt> resultEntrustAccountsList = new ArrayList<>();
        for(EntrustAccountExt dbEntrustAccount : dbEntrustAccountses){
            boolean isFounded = false;
            for (EntrustAccounts fbEntrustAccount : fbActiveEntrustAccountList) {
                logger.info("db fbAccountId:"+dbEntrustAccount.getFbAccountId());
                logger.info("fb accountId:"+fbEntrustAccount.getAccountId());
                if(dbEntrustAccount.getFbAccountId().toString().equals(fbEntrustAccount.getAccountId())){
                    isFounded = true;
                    break;
                }
            }
            if( !isFounded ){
                resultEntrustAccountsList.add(dbEntrustAccount);
            }
        }
        return resultEntrustAccountsList;
    }

    @Override
    public List<EntrustAccountExt> paramToEntrustAccount(List<Long> dbEntrustIds, String accessToken) throws FromFBEntrustAccountsIsNullException {

        List<EntrustAccounts> entrustAccountses = new ArrayList<EntrustAccounts>();
        List<EntrustAccounts> entrustAccountList = oauthFbService.getEntrustAccountsByFbUser(accessToken, 0, CommonConstants.NUM_INT_500, true);

        if (entrustAccountList == null || entrustAccountList.size() == 0) {
            logger.error("get data from facebook entrustAccounts is empty!");
            throw new FromFBEntrustAccountsIsNullException();
        }

        dbEntrustIds.forEach(id->{
            for (EntrustAccounts entrustAccounts : entrustAccountList) {
                if (entrustAccounts.getAccountId().equals(id.toString())) {
                    entrustAccountses.add(entrustAccounts);
                    break;
                }
            }
        });

       return this.FBEntrustAccount2EntrustAccount(entrustAccountses);
    }

    @Override
    public String addFbUser(String code, String errorCode, String errorMessage, String userId) throws BusinessException {

        String accessToken = null;
        ErrorType errorType = null;
        String flag = null;
        try {
            if (StringUtil.isEmpty(errorCode) && StringUtil.isNotEmpty(code)) {
                accessToken = getAccessToken(code);
                if (StringUtil.isEmpty(accessToken)) {
                    errorType = ErrorType.GET_ACCESS_TOKEN_FAIL;
                } else {
                    FBMe fbMe = oauthFbService.getFbMe(accessToken);
                    FbUser fbUser = fbUserService.getFbUser(Long.valueOf(fbMe.getId()));
                    if(fbUser==null){
                        flag = CommonConstants.FB_USER_ADD_FLAG+fbMe.getId();
                        if (!redisClient.setNx(flag, fbMe.getId(),5, TimeUnit.MINUTES.name())) {
                            errorType = ErrorType.YOUR_ACCOUNT_IS_RELATING;
                        }else{
                            try {
                                redisClient.expire(flag, 5, TimeUnit.MINUTES.name());
                                fbUser = new FbUser();
                                fbUser.setAccessToken(accessToken);
                                fbUser.setEmail(fbMe.getEmail());
                                fbUser.setFbUserId(Long.valueOf(fbMe.getId()));
                                fbUser.setFirstName(fbMe.getFirstName());
                                fbUser.setGender(fbMe.getGender());
                                fbUser.setLastName(fbMe.getLastName());
                                fbUser.setLink(fbMe.getLink());
                                fbUser.setLocale(fbMe.getLocale());
                                fbUser.setName(fbMe.getName());
                                String strOffset = fbMe.getTimezone()>=0?("+"+String.format("%02d", fbMe.getTimezone())+":00"):(String.format("%03d", fbMe.getTimezone())+":00");
                                fbUser.setTimeZone("GMT"+strOffset);
                                fbUser.setUpdatedTime(DateUtil.FbDateToJavaDate(fbMe.getUpdatedTime()).getTime());
                                fbUser.setUserId(userId);
                                fbUser.setVerified(1);
                                fbUserService.saveFbUser(fbUser);
//                              //保存账号信息
                                this.syncEntrustAccountInformation(accessToken, userId);
                            }catch (Exception e) {
                                errorType = ErrorType.INTERNAL_EXCEPTION;
                                logger.error(e.getMessage(), e);
                            }finally {
                                redisClient.delete(flag);
                            }
                        }
                    } else {
                        errorType = ErrorType.YOUR_ACCOUNT_HAS_BEEN_RELATED;
                    }

                }
            } else {
                errorType = ErrorType.FACEBOOK_OAUTH_ERROR;
                if (StringUtil.isNotEmpty(errorMessage)) {
                }
            }
        } catch (GetAccessTokenException e) {
            errorType = ErrorType.GET_ACCESS_TOKEN_FAIL;
        } catch (Exception e) {
            errorType = ErrorType.INTERNAL_EXCEPTION;
            logger.error(e.getMessage(), e);
        }

        if (null != errorType) {
            throw new BusinessException( errorType);
        }

        return accessToken;
    }


    /**
     * @discription
     * @author giser.yang
     * 创建时间 2015年6月5日 下午4:29:43
     * @param
     * @return
     * @throws BusinessException
     */

    @Override
    public boolean syncNewEntrustInformation(String userId, List<EntrustAccountExt> newFacebookEntrustAccounts,List<Long> newAddIds) throws BusinessException {
        ErrorType errorType = null;
        String flag = null;
        try {
            FbUser fbUser = fbUserService.getFbUser(userId);
            if( fbUser==null ){
                errorType = ErrorType.GET_ACCESS_TOKEN_FAIL;
            }else{
                String accessToken = fbUser.getAccessToken();
                if (StringUtil.isEmpty(accessToken)) {
                    errorType = ErrorType.GET_ACCESS_TOKEN_FAIL;
                } else {
                    FBMe fbMe = oauthFbService.getFbMe(accessToken);
                    flag = CommonConstants.FB_USER_ADD_FLAG + fbMe.getId();
                    if (!redisClient.setNx(flag, fbMe.getId(),5, TimeUnit.MINUTES.name())) {
                        errorType = ErrorType.YOUR_ACCOUNT_IS_RELATING;
                    }else{
                        try {

                            //初始化要用的的变量
                            List<AdAccountPermission> adaccountPpermissionList = new ArrayList<>();
                            Map<Long,AdAccountPermission> mapUserPermissions = new HashMap<Long, AdAccountPermission>();
                            List<PagePermission> pagePermissionList = new ArrayList<>();
                            List<EntrustAccountExt> updateAccounts = new ArrayList<>();
                            List<EntrustAccountExt> recycleAccounts = new ArrayList<>();
                            List<EntrustAccount> recycleAccountsDB = new ArrayList<>();
                            List<EntrustAccount> entrustAccountDBs = new ArrayList<>();


                            //返回当前用户下的所有bm账号
                            List<BusinessManager> businessManagers = syncBusinessManager(accessToken);

                            //删除无效的bm
                            List<EntrustAccountExt> entrustAccountExts = deleteInvalidBusinessManage(userId, fbUser, businessManagers);
                            recycleAccounts.addAll(entrustAccountExts);

                            //facebook广告账号
                            List<EntrustAccounts> facebookEntrustAccountes = oauthFbService.getEntrustAccountsByFbUser(accessToken, 0, CommonConstants.NUM_INT_500, true);
                            //带有扩展的facebook广告账号
                            List<EntrustAccountExt> facebookEntrustAccountExts = FBEntrustAccount2EntrustAccount(facebookEntrustAccountes);

                            if(!CollectionUtils.isEmpty(facebookEntrustAccountExts)){
                                for(EntrustAccountExt entrustAccountExt:facebookEntrustAccountExts){
                                    entrustAccountRdbService.saveorupdateAdaccount(entrustAccountExt);
                                }
                            }

                            //页面上选的需要新增的广告账号
                            List<Long> facebookAccountIds = new ArrayList<>();

                            if (!CollectionUtils.isEmpty(businessManagers)) {
                                // 同步BM信息以及BM下账号信息
                                for (BusinessManager businessManager : businessManagers) {

                                    //用户权限
                                    List<UserpermissionsResult> userpermissionsResults = oauthFbService.getUserpermissionsResult(accessToken, businessManager.getFbBusinessesId(), 0, CommonConstants.NUM_INT_500, true);
                                    //广告权限列表
                                    adaccountPpermissionList.addAll(this.getAdaccountPpermissionList(businessManager, fbUser.getFbUserId(), userpermissionsResults));
                                    //页面权限
                                    pagePermissionList = this.getPagePermissionList(accessToken, businessManager, fbUser.getFbUserId(), userpermissionsResults);

                                    if (facebookEntrustAccountes == null || facebookEntrustAccountes.size() == 0) {
                                        logger.error("get data from facebook entrustAccounts is empty!");
                                        throw new FromFBEntrustAccountsIsNullException();
                                    }

                                    List<EntrustAccountExt> dbEntrustAccountList = entrustAccountService.findEntrustAccountsByUserId(userId, businessManager.getFbBusinessesId());

                                    for(AdAccountPermission adAccountPermission:adaccountPpermissionList){
                                        mapUserPermissions.put(adAccountPermission.getFbAccountId(), adAccountPermission);
                                    }

                                    //adaccountPpermissionList当前bm下的广告账号权限entrustAccountList,entrustAccountes 当前accessToken下所有用户权限
                                    updateAccounts.addAll(this.getUpdateEntrustAccount(mapUserPermissions, dbEntrustAccountList));
                                    recycleAccounts.addAll(this.getRecyleEntrustAccount(facebookEntrustAccountes,dbEntrustAccountList));

                                    if (!CollectionUtils.isEmpty(recycleAccounts)) {
                                        recycleAccounts.addAll(recycleAccounts);
                                    }

                                }
                            }


                            //数据库回收所有的广告账号
                            for (EntrustAccountExt entrustAccount : recycleAccounts) {
                                //逻辑删除
                                AdAccountPermission userPermission = fbUserPermissionRdbService.getUserpermission(entrustAccount.getFbAccountId(), fbUser.getFbUserId(), entrustAccount.getFbBusinessId());
                                if (userPermission != null) {
                                    userPermission.setStatus("DISABLE");
                                    fbUserPermissionRdbService.saveorupdateUserPermission(userPermission);
                                    assetsAlertProducerService.notificationAccountAlert(fbUser.getUserId(),userPermission.getFbAccountId(), AccountOperateType.DELETE);
                                }
                            }

                            //数据库更新所有的广告账号
                            for (EntrustAccountExt entrustAccount : updateAccounts) {
                                AdAccountPermission userPermission = fbUserPermissionRdbService.getUserpermission(entrustAccount.getFbAccountId(), fbUser.getFbUserId(), entrustAccount.getFbBusinessId());
                                if (userPermission != null) {
                                    userPermission.setRole(entrustAccount.getRole());
                                    userPermission.setStatus(entrustAccount.getStatus());
                                    fbUserPermissionRdbService.saveorupdateUserPermission(userPermission);
                                    assetsAlertProducerService.notificationAccountAlert(fbUser.getUserId(),userPermission.getFbAccountId(), AccountOperateType.UPDATE);
                                }
                            }


                            //页面上选的需要新增的广告账号
                            for(EntrustAccount entrustAccount:newFacebookEntrustAccounts){
                                facebookAccountIds.add(entrustAccount.getFbAccountId());
                            }

                            //更新账号用户BM关系
                            for(AdAccountPermission userPermission:adaccountPpermissionList){
                                if(facebookAccountIds.contains(userPermission.getFbAccountId())){
                                    fbUserPermissionRdbService.saveorupdateUserPermission(userPermission);
                                    assetsAlertProducerService.notificationAccountAlert(fbUser.getUserId(),userPermission.getFbAccountId(), AccountOperateType.UPDATE);
                                }
                            }

                            //保存更新删除主页权限
                            this.saveorupdatedeletePagePermission(pagePermissionList, fbUser.getFbUserId(),accessToken);

                            recycleAccounts.forEach(account->{
                                recycleAccountsDB.add(account);
                            });

                            newFacebookEntrustAccounts.forEach(account->{
                                entrustAccountDBs.add(account);
                            });

                            updateAccounts.forEach(account->{
                                entrustAccountDBs.add(account);
                            });

//                            if(!CollectionUtils.isEmpty(newFacebookEntrustAccounts)){
//                                try {
//                                    logger.info("开始同步账号信息");
//                                    syncAccountInfoService.syncAccountInfo(userId);
//                                    logger.info("同步账号信息结束");
//                                } catch (Exception e) {
//                                    logger.error(e.getMessage(), e);
//                                }
//                            }

                            //更新账号详情
                            if(!CollectionUtils.isEmpty(newAddIds)){
                                for(Long fbAccountId:newAddIds){
                                    logger.info("新增账号{},同步一次所有业务 数据!",fbAccountId);
                                    Map<String, String> equipment = new HashMap<>();
                                    equipment.put("entrustAccountId", fbAccountId + "");
                                    equipment.put("accessToken", accessToken);
                                    equipment.put("pageNow", "" + 1);
                                    equipment.put("after", "");
                                    equipment.put("reTimes",0+"");
                                    equipment.put("className", FbAdgroup.class.getName());
                                    List<Filter> filters = new ArrayList<>();
                                    Filter filter = new Filter();
                                    filter.setOperator(FBOperator.GREATER_THAN.name());
                                    filter.setValue(0);
                                    filters.add(filter);
                                    equipment.put("filtering",JSONObject.toJSONString(filters));
                                    logger.info("开始拉取{}所有业务 数据!",fbAccountId);
                                    filter.setField("ad.updated_time");
                                    adGroupFBService.getAllAdGroupByAccountv2_4(equipment);
                                }
                            }
                            entrustAccountService.saveOrUpdateEntrust(recycleAccountsDB, entrustAccountDBs, userId,newAddIds);
                        } catch (IsNotBusinessesManagerException e) {
                            errorType = ErrorType.IS_NOT_BUSINESSES_MANAGER_EXCEPTION;
                        } catch (Exception e) {
                            errorType = ErrorType.INTERNAL_EXCEPTION;
                            logger.error(e.getMessage(), e);
                        } finally {
                            redisClient.delete(flag);
                        }
                    }
                }
            }

        } catch (Exception e) {
            errorType = ErrorType.INTERNAL_EXCEPTION;
            logger.error(e.getMessage(), e);
        }

        if (null != errorType) {
            throw new BusinessException( errorType);
        }

        return true;
    }

    private List<EntrustAccountExt> deleteInvalidBusinessManage(String userId, FbUser fbUser, List<BusinessManager> businessManagers) {

        //删除掉用户已经不在的企业,这个操作只有在用户点了同步并确定之后进行
        List<EntrustAccountExt> recycleAccounts = new ArrayList<>();
        List<FbUserBusinessManager> fbUserBusinessManagerDbs = fbUserBusinessRdbService.getFbUserBusinessManagersByFbUserId(fbUser.getFbUserId());
        Map<Long,BusinessManager> bmMap = new HashMap<>();
        if(!CollectionUtils.isEmpty(businessManagers)){
            for(BusinessManager businessManager:businessManagers){
                bmMap.put(businessManager.getFbBusinessesId(), businessManager);
            }
        }

        if(!CollectionUtils.isEmpty(fbUserBusinessManagerDbs)){
            for(FbUserBusinessManager fbUserBusinessManager:fbUserBusinessManagerDbs){
                if(!bmMap.containsKey(fbUserBusinessManager.getFbBusinessId())){
                    fbUserBusinessRdbService.deleteFbUserBusinessManager(fbUser.getFbUserId(), fbUserBusinessManager.getFbBusinessId());
                    //数据库有facebook没有
                    //获取当前bm下该用户的广告账号
                    List<EntrustAccountExt> entrustAccountList = entrustAccountService.findEntrustAccountsByUserId(userId, fbUserBusinessManager.getFbBusinessId());
                    if(!CollectionUtils.isEmpty(entrustAccountList)){
                        //回收所有的广告账号
                        recycleAccounts.addAll(entrustAccountList);
                    }
                }
            }
        }

        return recycleAccounts;
    }

    private void saveorupdatedeletePagePermission(List<PagePermission> pagePermissionList,Long fbUserId,String token){

        List<PagePermission> pagePermissionDBList = pagePermissionRdbService.getPagePermissionsByFbUserId(fbUserId);
        Map<String,PagePermission> pagePermissionMap = new HashMap<>();
        for(PagePermission pagePermission:pagePermissionList){
            pagePermissionMap.put(""+pagePermission.getPageId()+pagePermission.getFbBusinessId()+pagePermission.getFbUserId(), pagePermission);
        }
        for(PagePermission pagePermissionDB:pagePermissionDBList){
            if(!pagePermissionMap.containsKey(""+pagePermissionDB.getPageId()+pagePermissionDB.getFbBusinessId()+pagePermissionDB.getFbUserId())){
                //数据库里面存在,Fb不存在的
                pagePermissionRdbService.deletePagePermission(pagePermissionDB);
                FbUser fbUser = fbUserRdbService.getFbUserByFbUserId(pagePermissionDB.getFbUserId());
                assetsAlertProducerService.notificationPageAlert(fbUser.getUserId(),null,null,pagePermissionDB.getPageId(),PageOperateType.ADD);
            }
        }
        for(PagePermission pagePermission:pagePermissionList){
            pagePermissionRdbService.saveorupdatePagePermission(pagePermission);
            FbUser fbUser = fbUserRdbService.getFbUserByFbUserId(pagePermission.getFbUserId());
            if(pagePermissionRdbService.getPagePermission(pagePermission.getPageId(),pagePermission.getFbUserId(),pagePermission.getFbBusinessId()) !=null){
                //更新
                assetsAlertProducerService.notificationPageAlert(fbUser.getUserId(),null,null,pagePermission.getPageId(), PageOperateType.UPDATE);
            }else{
                //新增
                assetsAlertProducerService.notificationPageAlert(fbUser.getUserId(),null,null,pagePermission.getPageId(),PageOperateType.ADD);
            }
            syncAccountInfoService.syncPageInfo(pagePermission.getPageId(), token);
        }
    }

    /**
     * 更新fbuser
     *
     * @param code
     * @param errorCode
     * @param errorMessage
     * @param userId
     * @return
     * @throws BusinessException
     */
    @Override
    public String updateFbUser(String code, String errorCode, String errorMessage, String userId) throws BusinessException {

        String accessToken = null;
        ErrorType errorType = null;
        String flag = null;
        try {
            if (StringUtil.isEmpty(errorCode) && StringUtil.isNotEmpty(code)) {
                accessToken = getAccessToken(code);
                if (StringUtil.isEmpty(accessToken)) {
                    throw new BusinessException(ErrorCode.ERROR_60016);
                } else {
                    FBMe fbMe = oauthFbService.getFbMe(accessToken);
                    FbUser fbUser = fbUserService.getFbUser(Long.valueOf(fbMe.getId()));
                    flag = CommonConstants.FB_USER_ADD_FLAG + fbMe.getId();
                    if (!redisClient.setNx(flag, fbMe.getId(),5, TimeUnit.MINUTES.name())) {
                        errorType = ErrorType.YOUR_ACCOUNT_IS_RELATING;
                    } else {
                        if(!fbUser.getUserId().equals(userId)){
                            errorType = ErrorType.YOUR_ACCOUNT_HAS_BEEN_RELATED;
                        }else{
                            try {
                                redisClient.expire(flag, 1, TimeUnit.HOURS.name());
                                if (fbUser != null) {//如果有,则进行更新
                                    String dbAccessToken = fbUser.getAccessToken();//留存清空就token的缓存标识
                                    if (fbUser.getFbUserId().toString().equals(fbMe.getId())) {
                                        fbUser.setAccessToken(accessToken);
                                        fbUser.setEmail(fbMe.getEmail());
                                        fbUser.setFbUserId(Long.valueOf(fbMe.getId()));
                                        fbUser.setFirstName(fbMe.getFirstName());
                                        fbUser.setGender(fbMe.getGender());
                                        fbUser.setLastName(fbMe.getLastName());
                                        fbUser.setLink(fbMe.getLink());
                                        fbUser.setLocale(fbMe.getLocale());
                                        fbUser.setName(fbMe.getName());
                                        String strOffset = fbMe.getTimezone() >= 0 ? ("+" + String.format("%02d", fbMe.getTimezone()) + ":00") : (String.format("%03d", fbMe.getTimezone()) + ":00");
                                        fbUser.setTimeZone("GMT" + strOffset);
                                        fbUser.setUpdatedTime(DateUtil.FbDateToJavaDate(fbMe.getUpdatedTime()).getTime());
                                        fbUser.setUserId(userId);
                                        fbUser.setVerified(1);
                                        fbUser.setIsInvalid(0);
                                        fbUserService.updateFbUser(fbUser);
                                        fbUser.setAccessToken(dbAccessToken);
                                        fbUserRdbService.clearValidByFbUser(fbUser);
                                    } else {
                                        throw new BusinessException(ErrorCode.ERROR_60017);
                                    }
                                } else {
                                    throw new BusinessException(ErrorCode.ERROR_60016);
                                }

                            } catch (Exception e) {
                                errorType = ErrorType.INTERNAL_EXCEPTION;
                                logger.error(e.getMessage(), e);
                            } finally {
                                if (flag != null) {
                                    redisClient.delete(flag);
                                }
                            }
                        }
                    }
                }
            } else {
                errorType = ErrorType.FACEBOOK_OAUTH_ERROR;
                if (StringUtil.isNotEmpty(errorMessage)) {
                }
            }
        } catch (GetAccessTokenException e) {
            errorType = ErrorType.GET_ACCESS_TOKEN_FAIL;
        } catch (Exception e) {
            errorType = ErrorType.INTERNAL_EXCEPTION;
            logger.error(e.getMessage(), e);
        }

        if (null != errorType) {
            throw new BusinessException(errorType);
        }

        return accessToken;
    }
}
